package com.ssy.lingxi.dataauth.handler;

import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.dataauth.builder.PgSqlStatementBuilder;
import com.ssy.lingxi.dataauth.model.constant.DataAuthConstant;
import com.ssy.lingxi.dataauth.model.constant.DataAuthType;
import com.ssy.lingxi.dataauth.model.dto.ChannelAuthDto;
import com.ssy.lingxi.dataauth.model.dto.ChannelAuthMemberDto;
import com.ssy.lingxi.dataauth.model.dto.DataAuthDto;
import org.hibernate.resource.jdbc.spi.StatementInspector;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 要执行的sql语句拦截器
 * @author 万宁
 * @version 2.0.0
 * @date 2021-03-30
 */
public class DataAuthInspector implements StatementInspector {
    private static final long serialVersionUID = -7381534324771985211L;

    @Override
    public String inspect(String sql) {
        //Step 1: 只对select语句做数据权限校验
        if(!sql.startsWith("select") || sql.startsWith("select nextval")) {
            return sql;
        }

        //Step 2: 对应DataAuthAspect中的标记设置，如果Http属性中没有标记，则不做拦截
        //异步方法等，不会有Http请求，所以这里也要去掉
        if(RequestContextHolder.getRequestAttributes() == null) {
            return sql;
        }

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if(Objects.isNull(request.getAttribute(DataAuthConstant.HTTP_ATTRIBUTE_DATA_AUTH_FLAG))) {
            return sql;
        }

        //Step 3:从缓存中获取当前用户的数据权限、渠道权限配置，如果缓存为null，则不做权限验证
        String token = request.getHeader("token");
        String userId = request.getHeader("userId");
        String source = request.getHeader("source");
        String authUrl = String.valueOf(request.getAttribute(DataAuthConstant.HTTP_ATTRIBUTE_DATA_AUTH_URL_KEY));
        Object authObject = request.getAttribute(DataAuthConstant.HTTP_ATTRIBUTE_AUTH_TYPE_KEY);
        if(!StringUtils.hasLength(token) || !StringUtils.hasLength(userId) || !StringUtils.hasLength(source) || !StringUtils.hasLength(authUrl) || Objects.isNull(authObject)) {
            return sql;
        }

        //从Redis中查询数据权限配置的用户Id列表
        String dataAuthKey = String.format(DataAuthConstant.DATA_AUTH_CACHE_KEY_FORMAT, token, userId, source);
        DataAuthDto dataAuthDto = (DataAuthDto) RedisServiceHolder.redisUtils.hGet(dataAuthKey, authUrl, Constants.REDIS_USER_INDEX);
        List<Long> dataAuthUserIds = dataAuthDto == null ? new ArrayList<>() : dataAuthDto.getUserIds();

        //从Redis中查询“业务员管理”配置的下级会员列表
        String channelAuthKey = String.format(DataAuthConstant.CHANNEL_AUTH_CACHE_KEY_FORMAT, token, userId);
        ChannelAuthDto channelAuthDto = RedisServiceHolder.redisUtils.get(channelAuthKey, Constants.REDIS_USER_INDEX, ChannelAuthDto.class);
        List<ChannelAuthMemberDto> channelAuths = channelAuthDto == null ? new ArrayList<>() : channelAuthDto.getChannels();

        //拦截类型
        DataAuthType authType = DataAuthType.parse(Integer.valueOf(String.valueOf(authObject)));
        switch (authType) {
            case USER:
                if(CollectionUtils.isEmpty(dataAuthUserIds)) {
                    return sql;
                }
                break;
            case CHANNEL:
                if(CollectionUtils.isEmpty(channelAuths)) {
                    return sql;
                }
                break;
            default:
                if(CollectionUtils.isEmpty(dataAuthUserIds) && CollectionUtils.isEmpty(channelAuths)) {
                    return sql;
                }
                break;
        }

        String tableName = String.valueOf(request.getAttribute(DataAuthConstant.HTTP_ATTRIBUTE_TABLE_NAME_KEY));
        String memberIdColumnName = String.valueOf(request.getAttribute(DataAuthConstant.HTTP_ATTRIBUTE_MEMBER_ID_COLUMN_NAME_KEY));
        String roleIdColumnName = String.valueOf(request.getAttribute(DataAuthConstant.HTTP_ATTRIBUTE_ROLE_ID_COLUMN_NAME_KEY));
        String userIdColumnName = String.valueOf(request.getAttribute(DataAuthConstant.HTTP_ATTRIBUTE_USER_ID_COLUMN_NAME_KEY));

        //Step 4:拼接where语句，返回
        return PgSqlStatementBuilder.simpleSelectBuilder()
                .fromSql(sql)
                .specifyType(authType)
                .specifyTable(tableName)
                .addWhere(memberIdColumnName, roleIdColumnName, userIdColumnName, dataAuthUserIds, channelAuths)
                .buildSql()
                .showExecSql()
                .toSql();
    }
}
